package com.example.utils;
/**
 * 唯一性：不能重复
 * 有序性（可选）：便于排查或展示
 * 高性能和低冲突
 * 固定长度 20 位
 */

/**
 * 订单ID生成器类，用于生成唯一的订单ID
 */
public class OrderIdGenerator {
    // 节点ID，用于区分不同的节点
    private final long nodeId;
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;
    // 节点ID所占的位数
    private long nodeBits = 10L;
    // 序列号所占的位数
    private long sequenceBits = 12L;
    // 序列号的最大值
    private long maxSequence = ~(-1L << sequenceBits);

    // 节点ID左移的位数，等于序列号所占的位数
    private long nodeShift = sequenceBits;
    // 时间戳左移的位数，等于序列号和节点ID所占位数之和
    private long timestampLeftShift = sequenceBits + nodeBits;

    // 序列号
    private long sequence = 0L;

    /**
     * 构造函数，初始化节点ID
     * @param nodeId 节点ID
     * @throws RuntimeException 如果节点ID超出范围
     */
    public OrderIdGenerator(long nodeId) {
        if (nodeId > (~(-1L << nodeBits))) {
            throw new RuntimeException("节点ID超出范围");
        }
        this.nodeId = nodeId << sequenceBits;
    }

    public static void main(String[] args) {
        OrderIdGenerator idGen = new OrderIdGenerator(1); // 节点ID为1
        for (int i = 0; i < 10; i++) {
            System.out.println(idGen.generateOrderNo());
        }
    }

    /**
     * 生成下一个订单ID
     * @return 下一个订单ID
     * @throws RuntimeException 如果时钟回拨
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨");
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        lastTimestamp = timestamp;

        return (timestamp << timestampLeftShift)
                | nodeId
                | sequence;
    }

    /**
     * 等待直到获得一个更大的时间戳
     * @param lastTimestamp 上次的时间戳
     * @return 下一个时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 生成订单号作为字符串，并补全到20位
     * @return 订单号字符串
     */
    public String generateOrderNo() {
        return String.format("%020d", nextId());
    }
}

